package day0318.homework;

/**
 * @Author Stringzhua
 * @Date 2024/3/16 15:19
 * description:火车站窗口卖票场景
 * 使用多线程,模拟三个窗口同时售票100张
 * synchronized 解决窗口买票超卖问题
 *
 * SellTicket类中添加了一个静态变量instance和一个静态的锁对象lock。
 * getInstance()方法首先检查instance是否为null，
 * 如果是，则进入同步代码块。
 * 在同步代码块中再次检查instance是否为null，
 * 如果是，则创建一个新的SellTicket实例并赋值给instance。
 * 这样确保了只有一个实例被创建。
 * 同时，将SellTicket的构造函数设置为私有，
 * 以防止外部直接创建实例。在main方法中，
 * 通过调用getInstance()方法获取唯一的SellTicket实例，
 * 并启动三个线程来执行run()方法。
 * */
public class SellTicket {
    public static void main(String[] args) {
        //测试单例模式卖票,只有一个窗口卖票
        SellTicketWindows sellTicketWindows = new SellTicketWindows();
        new Thread(sellTicketWindows).start();//第 1 个线程-窗口
        new Thread(sellTicketWindows).start();//第 2 个线程-窗口
        new Thread(sellTicketWindows).start();//第 3 个线程-窗口
    }
}

//实现接口方式, 使用 synchronized 实现线程同步
class SellTicketWindows implements Runnable {
    private int ticketNum = 100;//让多个线程共享 ticketNum
    private boolean loop = true;//控制 run 方法变量
    private static SellTicketWindows instance;//静态变量,用于保存唯一实例
    private static Object lock = new Object();//锁对象,用于同步

    private static SellTicketWindows getInstance() {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new SellTicketWindows();
                }
            }
        }
        return instance;
    }

    @Override
    public void run() {
        while (loop) {
            sell();//sell 方法是一共同步方法
        }
    }

    //1. public synchronized void sell() {} 就是一个同步方法
    //2. 这时锁在 this 对象
    //3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在 this 对象
    public void sell() { //同步方法, 在同一时刻， 只能有一个线程来执行 sell 方法
        synchronized (this) {//也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在 this 对象
            if (ticketNum <= 0) {
                System.out.println(Thread.currentThread().getName() + "售票结束...");
                loop = false;
                return;
            }
            //休眠 50 毫秒, 模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2
        }
    }
}